﻿using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using NumSkull;

namespace NumSkullTests
{
    [TestFixture]
    [Category("Unit")]
    public class StatisticsTests
    {
        [Test]
        public void MeanReturnsDefaultWhenNull()
        {
            var mean = ((IEnumerable<double>)null).Mean();
            Assert.That(mean, Is.EqualTo(default(double)));
        }

        [Test]
        public void MeanReturnsDefaultWhenEmpty()
        {
            var mean = Enumerable.Empty<double>().Mean();
            Assert.That(mean, Is.EqualTo(default(double)));
        }

        [Test]
        public void DoesMeanCalculation()
        {
            var data = new[] { 1, 1, 3, 5, 7, 9, 11, 13, 15, 17d };
            var mean = data.Mean();
            Assert.That(mean, Is.EqualTo(8.2d).Within(.0001));
        }

        [Test]
        public void MedianReturnsDefaultWhenNull()
        {
            var median = ((IEnumerable<double>)null).Median();
            Assert.That(median, Is.EqualTo(default(double)));
        }

        [Test]
        public void MedianReturnsDefaultWhenEmpty()
        {
            var median = Enumerable.Empty<double>().Median();
            Assert.That(median, Is.EqualTo(default(double)));            
        }
        
        [Test]
        public void MedianSorts()
        {
            var data = new[] { 3, 1, 5, 4, 2, 9d };
            var median = data.Median();
            Assert.That(median, Is.EqualTo(3.5));
        }

        [Test]
        public void MedianCalculatesOddCardinality()
        {
            var data = new[] { 1, 2, 5, 7, 9d };
            var median = data.Median();
            Assert.That(median, Is.EqualTo(5));
        }

        [Test]
        public void MedianCalculatesEvenCardinality()
        {
            var data = new[] { 1, 2, 3, 5, 7, 9, 11, 13, 15, 17d };
            var median = data.Median();
            Assert.That(median, Is.EqualTo(8));
        }

        [Test]
        public void ModeReturnsDefaultWhenNull()
        {
            var mean = ((IEnumerable<double>)null).Mode();
            Assert.That(mean, Is.EquivalentTo(Enumerable.Empty<double>()));
        }

        [Test]
        public void ModeReturnsDefaultWhenEmpty()
        {
            var mean = Enumerable.Empty<double>().Mode();
            Assert.That(mean, Is.EquivalentTo(Enumerable.Empty<double>()));
        }

        [Test]
        public void DoesSingleModeCalculation()
        {
            var data = new[] { 1, 2, 3, 3, 4, 5, 6, 7, 8d };
            var mode = data.Mode();
            Assert.That(mode, Is.EquivalentTo(new[] { 3 }));
        }

        [Test]
        public void ReturnsHighestMode()
        {
            var data = new[] { 1, 2, 2, 2, 3, 5, 5, 8d };
            var mode = data.Mode();
            Assert.That(mode, Is.EquivalentTo(new[] { 2 }));
        }

        [Test]
        public void ReturnsEmptyMode()
        {
            var data = new[] { 1, 2, 3, 4, 5, 6, 7, 8d };
            var mode = data.Mode();
            Assert.That(mode, Is.EquivalentTo(Enumerable.Empty<double>()));
        }

        [Test]
        public void DoesMultipleModeCalculation()
        {
            var data = new[] { 1, 2, 2, 2, 3, 5, 5, 5, 8d };
            var mode = data.Mode();
            Assert.That(mode, Is.EquivalentTo(new[] { 2, 5 }));
        }

        [Test]
        public void VarianceReturnsDefaultWhenEmpty()
        {
            var variance = Enumerable.Empty<double>().Variance();

            Assert.That(variance, Is.EqualTo(default(double)));
        }

        [Test]
        public void DoesVarianceCalculation()
        {
            var data = new[] { 1, 1, 3, 5, 7, 9, 11, 13, 15, 17d };
            var mean = data.Variance();
            Assert.That(mean, Is.EqualTo(1093.404).Within(.001));
        }

        [Test]
        public void StandardDeviationReturnsDefaultWhenEmpty()
        {
            var variance = Enumerable.Empty<double>().StandardDeviation();

            Assert.That(variance, Is.EqualTo(default(double)));
        }

        [Test]
        public void DoesStandardDeviationCalculation()
        {
            var data = new[] { 1, 1, 3, 5, 7, 9, 11, 13, 15, 17d };
            var mean = data.StandardDeviation();
            Assert.That(mean, Is.EqualTo(33.066).Within(.001));
        }
    }
}